﻿using System;
using System.Threading;

namespace Game
{
    public class Engine
    {
        public event EventHandler TurnMovedToNextPlayer;

        public event EventHandler EndOfGame;

        public event EventHandler PlayerHasScored;
        
        private Board m_Board;
        private Player[] m_Players;
        private int m_CurrentPlayerIndex;      
        private Board.Coordinate m_LastCardCoordinate;
        
        public Engine(GameSettings i_Settings)
        {
            bool isBoardValid = Board.Make(i_Settings.Rows, i_Settings.Cols, out m_Board);
            if (!isBoardValid)
            {
                throw new System.Exception("Exception: Bad board parameters to Game c'tor.\n");
            }

            m_Players = new Player[2];
            Player.ePlayerType firstPlayer, secondPlayer;

            firstPlayer = Player.ePlayerType.Mortal;
            secondPlayer = i_Settings.IsSinglePlayer ? Player.ePlayerType.AI : Player.ePlayerType.Mortal;
            
            this.Player1 = new Player(i_Settings.Player1Name, firstPlayer);
            this.Player2 = new Player(i_Settings.Player2Name, secondPlayer);
            m_CurrentPlayerIndex = 0;

            if (m_Players[1].PlayerType == Player.ePlayerType.AI)
            {
                m_Players[1].Name = "Vader 2.0";
                m_Players[1].AI.RegisterAsListenerToCards(this.Board);
            }
        }

        public void TakeAMoveFromPlayer(Board.Coordinate i_Coord)
        {
            if (!Board.IsLegalMove(i_Coord))
            {
                throw new Exception("Illegal move");
            }

            Board.FlipCard(i_Coord);
            bool firstMove = m_LastCardCoordinate == null;

            if (firstMove)
            {
                m_LastCardCoordinate = i_Coord;
            }
            else 
            {
                Card firstCard = Board.GetCard(i_Coord);
                Card secondCard = Board.GetCard(m_LastCardCoordinate);

                int milisecondsToSleep = 1500;
                Thread.Sleep(milisecondsToSleep);

                if (firstCard.Equals(secondCard))
                {
                    Board.BurnCard(i_Coord);
                    Board.BurnCard(m_LastCardCoordinate);

                    this.CurrentPlayer.Score++;
                    if (PlayerHasScored != null)
                    {
                        PlayerHasScored(this, new EventArgs());
                    } 
                }
                else
                {
                    Board.FlipCard(i_Coord);
                    Board.FlipCard(m_LastCardCoordinate);

                    advanceTurn();
                }
                
                m_LastCardCoordinate = null;
                
                if (!isEndGame() && CurrentPlayer.PlayerType == Player.ePlayerType.AI)
                {
                    const bool v_FirstAIMove = true;

                    TakeAMoveFromPlayer(CurrentPlayer.AI.GetMove(this.Board, v_FirstAIMove));
                    TakeAMoveFromPlayer(CurrentPlayer.AI.GetMove(this.Board, !v_FirstAIMove));
                }
            }
        }
        
        private void advanceTurn()
        {
            m_CurrentPlayerIndex = (m_CurrentPlayerIndex + 1) % 2;
            TurnMovedToNextPlayer.Invoke(this, new EventArgs());
        }

        private bool isEndGame()
        {
            bool retVal = m_Board.IsAllBoardBurned();
            if (retVal && EndOfGame != null)
            {
                EndOfGame.Invoke(this, new EventArgs());
            }

            return retVal;
        }

        public Player Player1
        {
            get { return m_Players[0]; }
            private set { m_Players[0] = value; }
        }

        public Player Player2
        {
            get { return m_Players[1]; }
            private set { m_Players[1] = value; }
        }

        public Player CurrentPlayer
        {
            get { return m_Players[m_CurrentPlayerIndex]; }
        }

        public Board Board
        {
            get { return m_Board; }
            private set { m_Board = value; }
        }

        public void ResetPlayersScores()
        {
            foreach (Player currentPlayer in m_Players)
            {
                currentPlayer.Score = 0;
            }
        }
    }
}
